<template>
  <v-list>
    <v-list-item
      v-if="propItems.length > 0"
      ripple
      @mousedown.prevent
      @click="toggleAll"
    >
      <v-list-item-action>
        <v-icon :color="selectedSomeItem ? '#EB9B31' : ''">
          {{ icon }}
        </v-icon>
      </v-list-item-action>
      <v-list-item-content>
        <v-list-item-title class="text-left"> Select All </v-list-item-title>
      </v-list-item-content>
    </v-list-item>
    <v-menu
      :close-on-content-click="false"
      v-for="item in propItems"
      :key="item.id"
      offset-x
    >
      <template v-slot:activator="{ on }">
        <v-list-item @click="toggleItem(item, !isEverySelected(item))">
          <v-list-item-action>
            <v-icon :color="isSomeSelected(item) ? '#EB9B31' : ''">
              {{ getIcon(item) }}
            </v-icon>
          </v-list-item-action>
          <v-list-item-content>
            <v-list-item-title class="text-left">
              {{ item.text }}
            </v-list-item-title>
          </v-list-item-content>
          <v-list-item-icon>
            <v-btn v-on="on" icon v-if="item.children">
              <v-icon>mdi-chevron-right</v-icon>
            </v-btn>
          </v-list-item-icon>
        </v-list-item>
      </template>
      <TreeSelect
        :items="item.children"
        :selectedItem.sync="syncedSelectedItem"
      ></TreeSelect>
    </v-menu>
  </v-list>
</template>
<script lang="ts">
import { Vue, Component, PropSync } from "vue-property-decorator";
import SelectAll from "./SelectAll.vue";
interface IItem {
  id: string;
  text: string;
  children?: Array<IItem>;
}

@Component({
  components: {
    SelectAll,
  },
  props: {
    items: {
      type: Array,
      default: () => [],
    },
  },
})
export default class TreeSelect extends Vue {
  @PropSync("selectedItem", { type: Array, default: () => [] })
  syncedSelectedItem!: Array<IItem>;

  get propItems(): Array<IItem> {
    return this.$props.items;
  }

  // 激活时使用的图标
  onIcon = "mdi-checkbox-marked";
  // 不活动时使用的图标
  offIcon = "mdi-checkbox-blank-outline";
  // 未确定图标
  indeterminateIcon = "mdi-minus-box";

  // 判断传入的item是否被选中,有两种情况，一种是没有children的item，一种是有children的item
  isEverySelected(item: IItem) {
    // 有children的item判断是否全部被选中
    if (item.children) {
      const allChildren = this.getAllChild(item);

      return (
        allChildren.length > 0 &&
        allChildren.every((child) => {
          return this.syncedSelectedItem.includes(child);
        })
      );
    } else {
      // 没有children的item判断是否被选中
      return this.syncedSelectedItem.includes(item);
    }
  }

  // 判断传入的item是否被选中,有两种情况，一种是没有children的item，一种是有children的item
  isSomeSelected(item: IItem) {
    // 有children的item判断是否一些被选中
    if (item.children) {
      const allChildren = this.getAllChild(item);

      return allChildren.some((child) => {
        return this.syncedSelectedItem.includes(child);
      });
    } else {
      // 没有children的item判断是否被选中
      return this.syncedSelectedItem.includes(item);
    }
  }

  // 递归获取指定节点的所有子节点
  getAllChild(item: IItem): Array<IItem> {
    const children: Array<IItem> = [];
    // 有子节点
    if (item.children) {
      item.children.forEach((child) => {
        children.push(...this.getAllChild(child));
      });
      return children;
    } else {
      // 无子节点
      return [item];
    }
  }

  // selectAll
  toggleAll() {
    this.$nextTick(() => {
      // 取消全选
      if (this.selectedAllItem) {
        this.allChildren.forEach((item) => {
          const childIndex = this.syncedSelectedItem.indexOf(item);
          childIndex >= 0 && this.syncedSelectedItem.splice(childIndex, 1);
        });
      } else {
        // 全选
        this.allChildren.forEach((item) => {
          const childIndex = this.syncedSelectedItem.indexOf(item);
          childIndex < 0 && this.syncedSelectedItem.push(item);
        });
      }
    });
  }

  get allChildren() {
    const allChildren: IItem[] = [];
    this.propItems.forEach((item) => {
      allChildren.push(...this.getAllChild(item));
    });
    return allChildren;
  }

  get selectedAllItem() {
    // every在数组长度为0的时候会返回true所以还要判断一下数组的长度是否不为0
    return (
      this.allChildren.length > 0 &&
      this.allChildren.every((child) => {
        return this.syncedSelectedItem.includes(child);
      })
    );
  }

  get selectedSomeItem() {
    return this.allChildren.some((child) => {
      return this.syncedSelectedItem.includes(child);
    });
  }

  get icon() {
    if (this.selectedAllItem) return this.onIcon;
    if (this.selectedSomeItem) return this.indeterminateIcon;
    return this.offIcon;
  }

  // selectItem
  toggleItem(item: IItem, isSelectAll: boolean) {
    // 有子节点
    if (item.children) {
      item.children.forEach((child) => {
        this.toggleItem(child, isSelectAll);
      });
    } else {
      // 无子节点
      const itemIndex = this.syncedSelectedItem.indexOf(item);
      // 判断当前是进行全选还是全选全选
      if (isSelectAll) {
        // 全选
        // 将没选中的节点添加进选择列表
        if (itemIndex < 0) {
          this.syncedSelectedItem.push(item);
        }
      } else {
        // 取消全选
        // 将选中的节点从选择列表中移除
        if (itemIndex >= 0) {
          this.syncedSelectedItem.splice(itemIndex, 1);
        }
      }
    }
  }

  getIcon(item: IItem) {
    if (this.isEverySelected(item)) return this.onIcon;
    if (this.isSomeSelected(item)) return this.indeterminateIcon;
    return this.offIcon;
  }
}
</script>
<style lang="scss" scoped>
@import "./TreeSelect.scss";
</style>
